#pragma once

#include <iostream>
#include <string>

#include <sys/poll.h> //select函数
#include <cerrno>
#include <cstring>
#include "mydll/Sock.hpp"
#include "mydll/log.hpp"


namespace server
{
    using namespace TCP;
    static const int N = 4096; //pollfd数组初始大小
    static const int defaufd = -1; //初始化数组的fd 作为未使用标记
    static const int defauevent = 0; //初始化数组的event

    class selectserver
    {
        
    public:
        selectserver(int port = 8088)
            :ss(port)
            ,arrN(N)
            ,fdarray(nullptr)
        {
            fdarray = new struct pollfd[arrN];
        }

        void Init()
        {
            ss.Socket();
            ss.Bind();
            ss.Listen();

            for(int i = 0;i<N;++i) 
            {
                fdarray[i].fd = defaufd; //初始化sockfdfd管理数组
                fdarray[i].events = fdarray[i].revents = defauevent;
            }
        }


        void Start()
        {
            //本函数中不能直接accept立刻监听 而是靠select监视并给我们反馈 否则会阻塞在accept
            //网络中的新连接被当作读来处理

            while(true)
            {
                //因为rfds是输入输出参数 在一次调用后会被select修改里面的值返回给我们 所以我们要让select一直监听必须每次都修改
                //其次，maxfd每次也是在动态变化的，也需要动态更新

                fdarray[0].fd = ss.GetSockFd(); //0下标默认存储服务器sockfd持续监听连接
                fdarray[0].events = POLLIN; //标记为设置为可读

                int timeout = -1; //阻塞式
                int n = poll(fdarray,arrN,timeout);

                switch(n)
                {
                    case 0: //等待超时
                    {
                        logMessage(Debug, "timeout, %d: %s", errno, strerror(errno));
                        break;
                    }
                    case -1: //等待失败
                    {
                        logMessage(Warning, "%d: %s", errno, strerror(errno));
                        break;
                    }
                    default: //n>0表示有n个文件描述符可读取 但没有accept 在全连接队列中等待我们连接
                    {
                        logMessage(Debug, "有就绪事件发生了|连接数: %d", n);
                        HandlerEvent();
                        DebugPrint();
                        break;
                    }
                }
                sleep(1);
            }

        }
        void Accepter() //获取连接
        {
            std::cout << "有一个新连接到来了" << std::endl;
            //这里在进行Accept不会被阻塞
            std::string client_ip;
            uint16_t client_port;
            int client_fd = ss.Accept(&client_ip,&client_port);
            if(client_fd < 0) return; //获取失败则停止执行

            int pos = 1; //从1开始 0下标默认服务器fd
            //查找sockfd空位插入
            for(pos;pos<N;++pos) { if(fdarray[pos].fd == defaufd) break; }
            if(pos >= N) //文件描述符数组已满
            {
                //考虑扩容
                int tmparrN = arrN*(1.5);
                struct pollfd* tmp = (struct pollfd*)realloc(fdarray,tmparrN);
                if(tmp)
                {
                    arrN = tmparrN;
                    fdarray = tmp;
                }
                else
                {
                    //扩容失败
                    close(client_fd);
                    logMessage(Warning, "sockfd array[] full");
                }
                
            }
            else 
            {
                fdarray[pos].fd = client_fd;
                fdarray[pos].events = POLLIN; //(POLLIN | POLLOUT) 读写混合
                fdarray[pos].revents = defauevent;
            }
        }

        void HandlerEvent()
        {
            for(int i = 0;i<N;++i)
            {
                if(fdarray[i].fd == defaufd) continue; //如果是空fd则不予执行后面的判断
                //检查服务器套接字中位图是否可读 可连接accept 如果是服务器fd且在rfds中有连接 则进行连接处理
                if(fdarray[i].fd == ss.GetSockFd() && (fdarray->revents & POLLIN)) 
                {
                    Accepter(); //这里因为有连接条件 所以一定不会被阻塞
                }
                else if(fdarray[i].revents & POLLIN) //除了服务器fd，如果普通文件描述符有请求，则是有数据需要服务器读取
                {
                    char buf[1024] = {0}; //这里存在报文完整性无法保证的BUG
                    ssize_t n = recv(fdarray[i].fd,buf,sizeof(buf),0); //这里因为有读取请求则一定不会阻塞
                    if(n > 0)
                    {
                        if(buf[n-1] == '\n') buf[n-1] = '\0';
                        else buf[n] = '\0';
                        //fdarray[i].events |= POLLOUT; 加入写
                        std::cout<<"client: "<<buf<<std::endl;

                        std::string echo = "[server]:";
                        echo += buf;

                        send(fdarray[i].fd,echo.c_str(),echo.size(),0); //发送回去
                    }
                    else
                    {
                        if(n == 0) //对方断开连接
                            logMessage(Info,"Client quit... fd:%d",fdarray[i].fd);
                        else
                            logMessage(Warning,"recv error! Client fd:%d",fdarray[i].fd);

                        close(fdarray[i].fd); //关闭连接
                        fdarray[i].fd = defaufd; //该fd位置置为空状态
                        fdarray[i].events = fdarray[i].revents = defauevent;
                    }
                }
            }

        }
        void DebugPrint()
        {
            std::cout<<"fdarray: ";
            for(int i = 0;i<N;++i) if(fdarray[i].fd != defaufd) std::cout<<fdarray[i].fd<<" ";
            std::cout<<"\n";
        }

        ~selectserver() 
        {
             ss.Close_Sockfd(); 
             if(fdarray) delete[] fdarray;
        }
    private:
        SockServer ss;
        
        //v2
        size_t arrN;
        struct pollfd* fdarray; //sockfd管理数组
    };
}